package jp.co.ledinge.syllabarygame;

import java.io.File;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class BoxView extends View {
	private ArrayList<Point> mPointBuf = new ArrayList<Point>();
	private Point[] mSamplingPointBuf;
	byte[] mCharCode = null;
	private static final int SAMPLING_DX = 20;
	private static final int SAMPLING_DY = 20;
	private Paint mPaint;
	private Paint mSegmentPaint;
	private Point mP0;
	private Segment[] mSegmentBuf;
	// Viewクラスの定義
	public BoxView(Context context) {
		super(context);
	}

	public BoxView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mPaint = new Paint();
		mPaint.setColor(Color.BLUE);
		mPaint.setStyle(Paint.Style.FILL);
		mPaint.setStrokeWidth(3);
		mSegmentPaint = new Paint();
		mSegmentPaint.setColor(Color.GREEN);
		mSegmentPaint.setStyle(Paint.Style.FILL);
		mSegmentPaint.setStrokeWidth(3);
		mP0 = new Point(-1, -1);
	}
	public BoxView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
 	// 描画処理
	public void onDraw(Canvas canvas) {
		canvas.drawARGB(80, 255, 255, 255);
		mP0 = new Point(-1, -1); // 毎回newするのはなんとかしたいがなんともならない
		for (int i = 0; i < mPointBuf.size(); i++) {
			Point p1 = mPointBuf.get(i);
			if (mP0.x >= 0 && mP0.y >= 0) { // p0がPen upでない
				if (p1.x >= 0 && p1.y >= 0) { // p1がPen upでない
					canvas.drawLine(mP0.x, mP0.y, p1.x, p1.y, mPaint); // 2点からなる線を引く
				}
			}
			mP0 = p1;
		}
		if (mSamplingPointBuf != null) {
			for (Point point: mSamplingPointBuf) {
				canvas.drawCircle(point.x, point.y, 5, mPaint);
			}
		}
		if (mSegmentBuf != null) {
			for (Segment segment: mSegmentBuf) {
				mSegmentPaint.setColor(Color.BLUE);
				if (segment.state == Segment.PEN_UP) {
					mSegmentPaint.setColor(Color.GREEN);
				}
				canvas.drawLine(segment.mPoint0.x, segment.mPoint0.y, segment.mPoint1.x, segment.mPoint1.y, mSegmentPaint);
			}
		}
	}
	

	// タッチイベントの処理
	public boolean onTouchEvent(MotionEvent event) {
		int x = (int) event.getX();
		int y = (int) event.getY();
		mPointBuf.add(new Point(x, y));
		if (event.getAction() == MotionEvent.ACTION_UP) {
			mPointBuf.add(new Point(-1, -1)); // Pen upの場合, (-1,-1)を挿入
		}
		invalidate(); // 通知
		return true;
	}

	// 文字データの設定
	public void setCharCode(String str) {
		try {
			mCharCode = str.getBytes("utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

	// 座標データの保存処理
	public void savePenData(String fname) {
		// 座標データがない場合リターン
		if (mPointBuf.size() == 0) {
			return;
		}
		// 保存先の決定
		File fdir = new File("/mnt/sdcard/pendata/");
		fdir.mkdirs();
		String file = fdir.getAbsolutePath() + "/" + fname;
		Log.v("savePenData", file);
		// 手書きデータをファイルに書き込む
		try {
			FileOutputStream out = new FileOutputStream(file, true);
			int nByte = ((mPointBuf.size() + 1) * 2);
			int nPadding = 32 - nByte % 32;
			if (nPadding != 0) {
				nByte = ((nByte / 32) + 1) * 32;
			}
			// ヘッダ識別子(4bytes)を書き込む
			out.write(0xfe);
			out.write(0x01);
			out.write(0xfe);
			out.write(0x01);
			// ヘッダ + データのサイズ(2bytes)を書き込む
			out.write((byte) ((nByte >> 8) & 0xff));
			out.write((byte) (nByte & 0xff));
			// 文字コード(2bytes)を書き込む
			out.write(mCharCode[0]);
			out.write(mCharCode[1]);
			// ヘッダの残り部分を0でパディングする
			for (int i = 0; i < 24; i++) {
				out.write(0x00);
			}
			// x, yの順に座標データを書き込む
			for (int i = 0; i < mPointBuf.size(); i++) {
				out.write(mPointBuf.get(i).x);
				out.write(mPointBuf.get(i).y);
				String str = String.format("X=0x%02x, Y=0x%02x",
						mPointBuf.get(i).x, mPointBuf.get(i).y);
				Log.v("Point", str);
			}
			// 文字終了コードを書き込む
			out.write(0xfc);
			out.write(0xfc);
			// ブロックの残り部分を0でパディングする.
			for (int i = 0; i < nPadding; i++) {
				out.write(0x00);
			}
			out.flush();
			out.close();
		} catch (Exception e) {
			Log.v("Error", "File can't open");
		}
	}

	public void clearDrawList() {
		mPointBuf.clear();
		mSamplingPointBuf = null;
		mSegmentBuf = null;
		invalidate();
	}
	
	public Point[] samplingPoint() {
		Point[] point = samplingPoint(mPointBuf);
		invalidate();
		return point;
	}
	public void drawSamplingPoint(Point[] point) {
		mSamplingPointBuf = point;
		invalidate();
	}	
	public void drawPattern(Pattern pattern) {
		mSamplingPointBuf = pattern.points;
		mSegmentBuf = pattern.segments;
		invalidate();
	}	
	/**
	 * 同一点及び近似点の除去を行う.
	 * 
	 * @param inPoints
	 */
	private Point[] samplingPoint(ArrayList<Point> inPoints) {
		int count = inPoints.size();
		Point[] pts = new Point[count];
		if (count == 0) {
			return pts;
		}

		count--; // Pen up除く
		int idx = 0;
		Point p0 = new Point(inPoints.get(0));
		pts[idx++] = p0; // ストロークの始点をセット
		if (count > 1) { // 座標点が2点以上の場合
			for (int i = 1; i < count; i++) {
				Point p1 = new Point(inPoints.get(i));
				if ((Math.abs(p0.x - p1.x) >= SAMPLING_DX)
						|| (Math.abs(p0.y - p1.y) >= SAMPLING_DY)) {
					pts[idx++] = p1; // サンプル点とする
					p0 = p1;
				} else {
					if (i == (count - 1)) {
						if (idx == 1) { // サンプル点が始点のみの場合
							if (p1.x == p0.x && p1.y == p0.y) {
								pts[idx++] = new Point(p1.x + SAMPLING_DX, p1.y
										+ SAMPLING_DY);
							} else {
								pts[idx++] = p1;
							}
						} else { // 1個以上のサンプル点がある場合
							if (p1.x != p0.x || p1.y != p0.y) {
								pts[idx++] = p1;
							}
						}
					}
				}
			}
		} else { // 座標点が1点の場合
			pts[idx++] = new Point(p0.x + SAMPLING_DX, p0.y + SAMPLING_DY);
		}
		pts[idx++] = new Point(Stroke.PEN_UP, Stroke.PEN_UP); // Pen up追加
		Point[] tmpPoints = new Point[idx];
		System.arraycopy(pts, 0, tmpPoints, 0, idx);
		return tmpPoints;
	}
}
